using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;
using System.Collections;
using System.Data;

using MSHelpLib;
using MSHelpLib.BaseCode;

namespace MSHelpLib.BaseCode
{
	/// <summary>
	/// Summary description for Search.
	/// </summary>
	public class Search
	{
		private Hashtable SearchIndex=null;
        /// <summary>
        /// 
        /// </summary>
		public ArrayList FileList=null;		

		private IHelpFile m_HelpFile=null;
        /// <summary>
        /// Initializes a new instance of the <see cref="Search"/> class.
        /// </summary>
        /// <param name="helpFile">The help file.</param>
		public Search(IHelpFile helpFile)
		{			
			m_HelpFile=helpFile;
			ResetSearch();
		}

        /// <summary>
        /// Initializes a new instance of the <see cref="Search"/> class.
        /// </summary>
		public Search()
		{
			ResetSearch();
		}

        /// <summary>
        /// Resets the search.
        /// </summary>
		public void ResetSearch()
		{
			SearchIndex=new Hashtable();
			FileList=new ArrayList();
		}
								
		private int m_FileCount=0;
		private MSHelpLib.PAFEnum IndexFile(IFileInfo fileInfo)
		{
			m_FileCount++;			

			// Continue if fileInfo is not a HTM file of some sort.
			if ((fileInfo==null) || (fileInfo.Length==0) || (Path.GetExtension(fileInfo.FileName.ToLower()).EndsWith(".htm")==false))
				return MSHelpLib.PAFEnum.Continue;
						
			m_HelpFile.OnProgress(m_FileCount);
			m_HelpFile.OnStatus(1,"Processing "+fileInfo.FileName);

			// Add file to list.
			int FileID=FileList.Add(fileInfo);			
			string text=fileInfo.HelpStream.HelpFile.HelpSystem.ExtractAsText(fileInfo).ToLower();
			text=HttpUtility.HtmlDecode(text);

			// Remove all HTML tags
			// <[^>]*>					
			Regex reg1 = new Regex("<[^>]*>",RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline| RegexOptions.IgnoreCase);
			text=reg1.Replace(text,"");
					
			StringBuilder sb=new StringBuilder(text);			
			sb.Replace("&nbsp","");
			sb.Replace("\r"," ");
			sb.Replace("\n"," ");
			sb.Replace(" a "," ");
			sb.Replace(" in "," ");
			sb.Replace(" by "," ");
			sb.Replace(" or "," ");
			sb.Replace(" if "," ");
			sb.Replace(" of "," ");
			sb.Replace(" any "," ");
			sb.Replace(" but "," ");
			sb.Replace(" yes "," ");
			sb.Replace(" no "," ");
			sb.Replace(" on "," ");
			sb.Replace(" \t "," ");
			sb.Replace(" to "," ");
			sb.Replace(" and "," ");
			sb.Replace(" the "," ");
			sb.Replace(" now "," ");
			sb.Replace(" as "," ");
			text=sb.ToString();
			
			string word="";
			for(int i=0;i<text.Length;i++)
			{
				char ch=text[i];
				
				if ((Char.IsLetterOrDigit(ch)) || ch=='.')
					word+=ch;
				else if (Char.IsWhiteSpace(ch))	
				{
					word=word.Trim();
					if (word!="")
					{
						if (SearchIndex.ContainsKey(word))
						{
							// get ArrayList for this word
							ArrayList WordList=(ArrayList)SearchIndex[word];					
							
							// Add FileID 			
							bool bFound=false;
							foreach(DictionaryEntry deItem in WordList)
							{
								int Key=(int)deItem.Key;
								if (Key==FileID)
								{
									bFound=true;
									int Value=(int)deItem.Value;

									WordList.Remove(deItem);

									Value++;
									WordList.Add(new DictionaryEntry(Key,Value));
									
									break;
								}
							}
							if (!bFound)
							{
								WordList.Add(new DictionaryEntry(FileID,1));
							}
						}
						else
						{
							ArrayList WordList=new ArrayList();
							WordList.Add(new DictionaryEntry(FileID,1));
							SearchIndex.Add(word,WordList);
						}
					}
					word="";
				}                
			}			
			
			return MSHelpLib.PAFEnum.Continue;
		}

        /// <summary>
        /// Indexes the help file.
        /// </summary>
		public void IndexHelpFile()
		{			
			if (File.Exists(m_HelpFile.FInfo.FullName))
			{
				m_HelpFile.OnProgress(0,m_HelpFile.NumberOfFiles);
				m_HelpFile.OnStatus(0,"Indexing "+m_HelpFile.FInfo.Name);			
				m_FileCount=0;
				m_HelpFile.ProcessAllFiles(new ProcessAllFilesDelegate(IndexFile));			
				m_HelpFile.OnProgress(0,100);
			}
		}

        /// <summary>
        /// Searches for.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
		public SearchResultsTable SearchFor(string text)
		{
			string [] words=text.ToLower().Split(' ');			
			SearchResultsTable Results=null;
            bool bANDSearch = true;
						
			foreach(string word in words)
			{
				if (SearchIndex.ContainsKey(word))
				{
					ArrayList FoundList=(ArrayList)SearchIndex[word];

					if (Results==null)
					{
						Results=new SearchResultsTable();						
						foreach(DictionaryEntry deItem in FoundList)
						{
                            AddNewResult(Results, deItem);
						}						
						Results.AcceptChanges();
					}
					else
					{						
                        foreach (DictionaryEntry deFoundItem in FoundList)
                        {
                            int NewFileID = (int)deFoundItem.Key;
                            int WordHits = (int)deFoundItem.Value;

                            Results.DefaultView.RowFilter = "FileID=" + NewFileID.ToString();
                            if (Results.DefaultView.Count > 0)
                            {
                                SearchResultsRow row = (SearchResultsRow)Results.DefaultView[0].Row;
                                row.Rank += WordHits;
                            }
                            else
                            {
								// OR search - Search for all words, and merge results
								if (!bANDSearch)
								{
									// Add new row
									AddNewResult(Results, deFoundItem);
								}
							}
                        }

                        Results.DefaultView.RowFilter = "";
                        if (bANDSearch)
                        {
                            for (int i = Results.Rows.Count - 1; i >= 0; i--)
                            {
                                SearchResultsRow row = (SearchResultsRow)Results.Rows[i];
                                if (row.RowState == DataRowState.Unchanged)
                                    Results.Rows.RemoveAt(i);
                            }
                        }
                        Results.AcceptChanges();
					}
				}
				else
					break;
			}
			if (Results==null)
				return null;

			Results.DefaultView.RowFilter="";
			Results.DefaultView.Sort="rank";	
			
			return Results;
		}

        private DictionaryEntry AddNewResult(SearchResultsTable Results, DictionaryEntry deItem)
        {
            int FileID = (int)deItem.Key;
            int Value = (int)deItem.Value;

            SearchResultsRow row = Results.NewSearchResultsRow();
            row.Rank = Value;
            IFileInfo fi = (IFileInfo)FileList[FileID];
            row.URL = fi.HelpStream.HelpFile.UrlPrefix + fi.HelpStream.FileName + "::" + fi.FileName;
            row.Location = Path.GetFileName(fi.HelpStream.FileName);
            row.Title = Path.GetFileName(fi.FileName);
            row.FileID = FileID;

            Results.Rows.Add(row);
            return deItem;
        }

        /// <summary>
        /// Loads the dump.
        /// </summary>
        /// <param name="br">The br.</param>
		public void LoadDump(BinaryReader br)
		{			
			int FileCount=br.ReadInt32();
			for(int i=0;i<FileCount;i++)
			{
				string FileName=br.ReadString();
				IFileInfo fi=m_HelpFile.FindFile(FileName);
				FileList.Add(fi);
			}

			int HashCount=br.ReadInt32();
			for(int i=0;i<HashCount;i++)
			{								
				if (i % 20 ==0)
					m_HelpFile.OnProgress(i,HashCount);
				string Key=br.ReadString();
				int ListCount=br.ReadInt32();

				ArrayList list=new ArrayList();
				for(int j=0;j<ListCount;j++)
				{					
					int FileID=br.ReadInt32();
					int Value=br.ReadInt32();

					list.Add(new DictionaryEntry(FileID,Value));					
				}
				SearchIndex.Add(Key,list);
			}
		}

        /// <summary>
        /// Saves the dump.
        /// </summary>
        /// <param name="bw">The bw.</param>
		public void SaveDump(BinaryWriter bw)
		{
			bw.Write((Int32)FileList.Count);
			foreach(IFileInfo fi in this.FileList)
			{				
				bw.Write(fi.FileName);
			}

			bw.Write((Int32)SearchIndex.Count);
			System.Collections.IDictionaryEnumerator SearchEnum=this.SearchIndex.GetEnumerator();
			while (SearchEnum.MoveNext())
			{
				bw.Write(SearchEnum.Key.ToString());

				ArrayList list=(ArrayList)SearchIndex[SearchEnum.Key.ToString()];
				bw.Write((Int32)list.Count);

				foreach(System.Collections.DictionaryEntry de in list)
				{
					bw.Write((Int32)de.Key);
					bw.Write((Int32)de.Value);
				}
			}			
		}

	}
}
